﻿namespace PlugNT.Common
{
    using PlugNT.Safe;
    using System;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;

    public class Ip
    {
        private static bool fileIsExsit = true;
        private static string filePath = "";
        private static object lockHelper = new object();
        private static PHCZIP pcz = new PHCZIP();

        static Ip()
        {
            filePath = IConfig.ConfigPath + "ipdata.config";
            pcz.SetDbFilePath(filePath);
        }

        public static string GetAddress()
        {
            string result = String.Empty;
            result = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (result != null && result != String.Empty)
            {
                //可能有代理    
                if (result.IndexOf(".") == -1)    //没有"."肯定是非IPv4格式    
                    result = null;
                else
                {
                    if (result.IndexOf(",") != -1)
                    {
                        //有","，估计多个代理。取第一个不是内网的IP。    
                        result = result.Replace(" ", "").Replace("\"", "");
                        string[] temparyip = result.Split(",;".ToCharArray());
                        for (int i = 0; i < temparyip.Length; i++)
                        {
                            if (IsIPAddress(temparyip[i])
                                && temparyip[i].Substring(0, 3) != "10."
                                && temparyip[i].Substring(0, 7) != "192.168"
                                && temparyip[i].Substring(0, 7) != "172.16.")
                            {
                                return temparyip[i];    //找到不是内网的地址    
                            }
                        }
                    }
                    else if (IsIPAddress(result)) //代理即是IP格式    
                        return result;
                    else
                        result = null;    //代理中的内容 非IP，取IP    
                }
            }
            string IpAddress = (HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null && HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != String.Empty) ? HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] : HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
            if (null == result || result == String.Empty)
                result = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
            if (result == null || result == String.Empty)
                result = HttpContext.Current.Request.UserHostAddress;
            return result;
        }

        public static string GetAddressWithIP(string IPValue)
        {
            lock (lockHelper)
            {
                string result = pcz.GetAddressWithIP(IPValue.Trim());
                if (fileIsExsit)
                {
                    if (result.IndexOf("IANA") >= 0)
                    {
                        return "";
                    }
                    return result;
                }
                return null;
            }
        }

        public static string Help()
        {
            return "mailto:354990393@qq.com";
        }

        public static bool IsIPAddress(string str)
        {
            if ((((str == null) || (str == string.Empty)) || (str.Length < 7)) || (str.Length > 15))
            {
                return false;
            }
            string regformat = @"^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$";
            Regex regex = new Regex(regformat, RegexOptions.IgnoreCase);
            return regex.IsMatch(str);
        }

        public class CZ_INDEX_INFO
        {
            public uint IpEnd = 0;
            public uint IpSet = 0;
            public uint Offset = 0;
        }

        public class PHCZIP
        {
            protected bool bFilePathInitialized;
            protected string FilePath;
            protected FileStream FileStrm;
            protected uint Index_Count;
            protected uint Index_End;
            protected uint Index_Set;
            protected Ip.CZ_INDEX_INFO Search_End;
            protected uint Search_Index_End;
            protected uint Search_Index_Set;
            protected Ip.CZ_INDEX_INFO Search_Mid;
            protected Ip.CZ_INDEX_INFO Search_Set;

            public PHCZIP()
            {
                this.bFilePathInitialized = false;
            }

            public PHCZIP(string dbFilePath)
            {
                this.bFilePathInitialized = false;
                this.SetDbFilePath(dbFilePath);
            }

            public void Dispose()
            {
                if (this.bFilePathInitialized)
                {
                    this.bFilePathInitialized = false;
                    this.FileStrm.Close();
                }
            }

            public string GetAddressWithIP(string IPValue)
            {
                if (!this.bFilePathInitialized)
                {
                    return "";
                }
                this.Initialize();
                uint ip = this.IPToUInt32(IPValue);
                while (true)
                {
                    this.Search_Set = this.IndexInfoAtPos(this.Search_Index_Set);
                    this.Search_End = this.IndexInfoAtPos(this.Search_Index_End);
                    if ((ip >= this.Search_Set.IpSet) && (ip <= this.Search_Set.IpEnd))
                    {
                        return this.ReadAddressInfoAtOffset(this.Search_Set.Offset);
                    }
                    if ((ip >= this.Search_End.IpSet) && (ip <= this.Search_End.IpEnd))
                    {
                        return this.ReadAddressInfoAtOffset(this.Search_End.Offset);
                    }
                    this.Search_Mid = this.IndexInfoAtPos((this.Search_Index_End + this.Search_Index_Set) / 2);
                    if ((ip >= this.Search_Mid.IpSet) && (ip <= this.Search_Mid.IpEnd))
                    {
                        return this.ReadAddressInfoAtOffset(this.Search_Mid.Offset);
                    }
                    if (ip < this.Search_Mid.IpSet)
                    {
                        this.Search_Index_End = (this.Search_Index_End + this.Search_Index_Set) / 2;
                    }
                    else
                    {
                        this.Search_Index_Set = (this.Search_Index_End + this.Search_Index_Set) / 2;
                    }
                }
            }

            private uint GetOffset()
            {
                return BitConverter.ToUInt32(new byte[] { (byte) this.FileStrm.ReadByte(), (byte) this.FileStrm.ReadByte(), (byte) this.FileStrm.ReadByte(), 0 }, 0);
            }

            protected byte GetTag()
            {
                return (byte) this.FileStrm.ReadByte();
            }

            protected uint GetUInt32()
            {
                byte[] TempByte4 = new byte[4];
                this.FileStrm.Read(TempByte4, 0, 4);
                return BitConverter.ToUInt32(TempByte4, 0);
            }

            protected Ip.CZ_INDEX_INFO IndexInfoAtPos(uint Index_Pos)
            {
                Ip.CZ_INDEX_INFO Index_Info = new Ip.CZ_INDEX_INFO();
                this.FileStrm.Seek((long) (this.Index_Set + (7 * Index_Pos)), SeekOrigin.Begin);
                Index_Info.IpSet = this.GetUInt32();
                Index_Info.Offset = this.GetOffset();
                this.FileStrm.Seek((long) Index_Info.Offset, SeekOrigin.Begin);
                Index_Info.IpEnd = this.GetUInt32();
                return Index_Info;
            }

            public void Initialize()
            {
                this.Search_Index_Set = 0;
                this.Search_Index_End = this.Index_Count - 1;
            }

            public uint IPToUInt32(string IpValue)
            {
                int i;
                string[] IpByte = IpValue.Split(new char[] { '.' });
                int nUpperBound = IpByte.GetUpperBound(0);
                if (nUpperBound != 3)
                {
                    IpByte = new string[4];
                    for (i = 1; i <= (3 - nUpperBound); i++)
                    {
                        IpByte[nUpperBound + i] = "0";
                    }
                }
                byte[] TempByte4 = new byte[4];
                for (i = 0; i <= 3; i++)
                {
                    if (this.IsNumeric(IpByte[i]))
                    {
                        TempByte4[3 - i] = (byte) (Convert.ToInt32(IpByte[i]) & 0xff);
                    }
                }
                return BitConverter.ToUInt32(TempByte4, 0);
            }

            protected bool IsNumeric(string str)
            {
                return ((str != null) && Regex.IsMatch(str, @"^-?\d+$"));
            }

            private string ReadAddressInfoAtOffset(uint Offset)
            {
                string country = "";
                string area = "";
                uint country_Offset = 0;
                byte Tag = 0;
                this.FileStrm.Seek((long) (Offset + 4), SeekOrigin.Begin);
                Tag = this.GetTag();
                if (Tag == 1)
                {
                    this.FileStrm.Seek((long) this.GetOffset(), SeekOrigin.Begin);
                    if (this.GetTag() == 2)
                    {
                        country_Offset = this.GetOffset();
                        area = this.ReadArea();
                        this.FileStrm.Seek((long) country_Offset, SeekOrigin.Begin);
                        country = this.ReadString();
                    }
                    else
                    {
                        this.FileStrm.Seek(-1, SeekOrigin.Current);
                        country = this.ReadString();
                        area = this.ReadArea();
                    }
                }
                else if (Tag == 2)
                {
                    country_Offset = this.GetOffset();
                    area = this.ReadArea();
                    this.FileStrm.Seek((long) country_Offset, SeekOrigin.Begin);
                    country = this.ReadString();
                }
                else
                {
                    this.FileStrm.Seek(-1, SeekOrigin.Current);
                    country = this.ReadString();
                    area = this.ReadArea();
                }
                return (country + " " + area);
            }

            protected string ReadArea()
            {
                switch (this.GetTag())
                {
                    case 1:
                    case 2:
                        this.FileStrm.Seek((long) this.GetOffset(), SeekOrigin.Begin);
                        return this.ReadString();
                }
                this.FileStrm.Seek(-1, SeekOrigin.Current);
                return this.ReadString();
            }

            protected string ReadString()
            {
                uint Offset = 0;
                byte[] TempByteArray = new byte[0x100];
                TempByteArray[Offset] = (byte) this.FileStrm.ReadByte();
                while (TempByteArray[Offset] != 0)
                {
                    Offset += 1;
                    TempByteArray[Offset] = (byte) this.FileStrm.ReadByte();
                }
                return Encoding.Default.GetString(TempByteArray).TrimEnd(new char[1]);
            }

            public bool SetDbFilePath(string dbFilePath)
            {
                if (dbFilePath == "")
                {
                    return false;
                }
                try
                {
                    this.FileStrm = new FileStream(dbFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                }
                catch
                {
                    return false;
                }
                if (this.FileStrm.Length < 8)
                {
                    this.FileStrm.Close();
                    return false;
                }
                this.FileStrm.Seek(0, SeekOrigin.Begin);
                this.Index_Set = this.GetUInt32();
                this.Index_End = this.GetUInt32();
                this.Index_Count = ((this.Index_End - this.Index_Set) / 7) + 1;
                this.bFilePathInitialized = true;
                return true;
            }
        }
    }
}

